package com.hyts.distlock.facade.redis;

import com.hyts.distlock.config.LockConfig;
import com.hyts.distlock.lock.RedisLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;

import java.time.Clock;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.UUID;

/**
 * project-name:coffice-toolbox
 * package-name:com.hyts.distlock.facade.redis
 * author:Libo/Alex
 * create-date:2019-05-11 00:24
 * copyright:libo-hyts-github
 * email:libo2dev@aliyun.com
 * description:此类主要用于: jedis客户端合成的分布式锁
 */
public class JedisPoolLock extends RedisLock<JedisPool> {


    Logger logger = LoggerFactory.getLogger(JedisPoolLock.class);

    /**
     * redisLock对象构造器
     * @param redisClient
     */
    public JedisPoolLock(JedisPool redisClient) {
        super(redisClient);
    }

    @Override
    public String tryLock(Long requireTime, Long lockExpireTimeout) {
        return tryLock(null,null,requireTime,lockExpireTimeout);
    }

    @Override
    public String tryLock( Long requireTime, Long lockExpireTimeout,String lockValue) {
        return tryLock(null,lockValue,requireTime,lockExpireTimeout);
    }

    @Override
    public String tryLock(String key, Long requireTime, Long lockExpireTimeout) {
        return tryLock(key,null,requireTime,lockExpireTimeout);
    }

    @Override
    public String tryLock(String key, String lockValue, Long requireTime, Long lockExpireTimeout) {
        Jedis connection = getLockObjectClient().getResource();
        if(lockExpireTimeout < 0){
            lockExpireTimeout = LockConfig.LOCK_EXPIRE_TIME_OUT;
        }
        if(requireTime < 0){
            requireTime = LockConfig.LOCK_REQUIRE_TIME_OUT;
        }
        if(key == null || "".endsWith(key)){
            key = UUID.randomUUID().toString();
        }
        if(lockValue == null || "".endsWith(key)){
            lockValue = UUID.randomUUID().toString();
        }
        Long endTime = Clock.system(ZoneId.systemDefault()).instant().
                plus(requireTime,ChronoUnit.SECONDS).getEpochSecond();
        String lockKey = LockConfig.LOCK_REDIS_PREFIX + UUID.randomUUID().toString();
        try {
            while(endTime > System.currentTimeMillis()){
                //设置建立lock锁
                if(connection.setnx(lockKey,lockValue) == 1){
                    connection.expire(lockKey,(int)(long)lockExpireTimeout);
                    return lockKey;
                }
                if(connection.ttl(lockKey) == -1){
                    connection.expire(lockKey,(int)(long)lockExpireTimeout);
                }
            }
        } catch (Exception e) {
            logger.error("执行加锁失败！",e);
            connection.del(lockKey);
            return null;
        } finally {
            if(connection != null){
                connection.close();
            }
        }
        return lockKey;
    }

    @Override
    public String unLock(String key, String lockValue) {
        //进行控制解锁
        if(key == null || "".equals(key)){
            throw new NullPointerException("传入的key不可以为空");
        }
        /*if(lockValue == null || "".equals(lockValue)){
            throw new NullPointerException("传入的lockValue不可以为空");
        }*/
        Jedis connection = getLockObjectClient().getResource();
        boolean cycle = true;
        try {
            while(cycle){
                connection.watch(key);
                if(connection.exists(key)){
                    Transaction transaction = connection.multi();
                    transaction.del(key);
                    List<Object> results = transaction.exec();
                    if (results == null) {
                        continue;
                    }
                }else{
                    cycle = false;
                }
                connection.unwatch();
            }
        } catch (Exception e) {
            logger.error("释放加锁失败！",e);
        } finally {
            if(connection != null){
                connection.close();
            }
        }

        return key;
    }

    @Override
    public String unLock(String key) {
        return unLock(key,UUID.randomUUID().toString());
    }
}
